;
(function() {

	var LANGUAGES = {}, arraySlice = Array.prototype.slice;

	var BLANK = String.BLANK, ESCAPER_4_C_LIKE = '\\', JOIN = '</li><li>';
	// 共通标签
	var CLOSE_SPAN = '</span>', COMMENT_SPAN = '<span class="comment">', STRING_SPAN = '<span class="string">', NUMBER_SPAN = '<span class="number">',
	// 常用标签
	CHAR_SPAN = '<span class="character">', KEYWORD_SPAN = '<span class="keyword">', REGEXP_SPAN = '<span class="regexp">', DOC_SPAN = '<span class="doc">',
	// 预置的各种内容
	PRE_FUNC_SPAN = '<span class="prefunc">', PRE_VAR_SPAN = '<span class="prevar">',
	// 各种符号
	BRACKET_SPAN = '<span class="bracket">', OPERATOR_SPAN = '<span class="operator">', XMLTAG_SPAN = '<span class="xmltag">',
	// 键值对（XML属性、CSS 等可用）
	ATTRIBUTE_SPAN = '<span class="attribute">', DATAVALUE_SPAN = '<span class="datavalue">';
	var DQUOTE = '"', QUOTE = "'", SLASH = '/', ASTERISK = '*', NL_N = '\n', AT = '@', AND = '&', SPACE = ' ', TAB = '\t', SHARP = '#';// 标准符号
	var COMMA = ",", SEMICOLON = ';', COLON = ':', LEFT_BRACKET = '{', RIGHT_BRACKET = '}', POINT = '.', HYPHON = "-", EQUALS = '=', ZERO_WIDTH = "\uFEFF";
	var LEFT_ANGLE = '<', RIGHT_ANGLE = '>', // XML转义前符号
	// HTML转义
	NBSP = '&nbsp;', HTML_4_NBSP = '&nbsp;&nbsp;&nbsp;&nbsp;', HTML_LT = '&lt;', HTML_GT = '&gt;', HTML_AND = '&amp;', HTML_QUOT = "&quot;", HTML_SHARP = '&#35;';
	// 正则表达式
	var SPACE_REGX = /( |\t|\r|\n|\v|\uFEFF)/, // 标准空白
	BRACEKT_REGX = /(\{|\}|\[|\]|\(|\))/, // 默认括号，使用 C 语言的括号（类 C 语言都可使用）
	OPERATOR_REGX_4_C_LIKE = /(=|\+|\-|\*|\/|\%|\&|\||\^|\!|\~|\?|\:|,|;|\.)/, //默认操作符（单字符，不计“->”这种多字符操作符），使用 C 语言的操作符（类 C 语言都可使用 ）
	// 数字的 ASCII 编号
	CHAR_CODE_0 = 0x0030, CHAR_CODE_9 = 0x0039,
	// 大写字母字母的 ASCII 编号
	CHAR_CODE_UPPER_A = 0x0041, CHAR_CODE_UPPER_Z = 0x005A, CHAR_CODE_UPPER_F = 0x0046,
	// 小写字母字母的 ASCII 编号
	CHAR_CODE_LOWER_A = 0x0061, CHAR_CODE_LOWER_Z = 0x007A, CHAR_CODE_LOWER_F = 0x0066,
	// 特殊符号的 ASCII 编号
	CHAR_CODE_UNDERBAR = 95, CHAR_CODE_DOLLAR = 36;

	function getLang(execute, plugIn, keywords) {

		if (arguments.length > 3) {
			keywords = arraySlice.call(arguments, 2);
		}

		return {
			getKeywords : function() {
				return keywords;
			},
			getPlugIn : function() {
				return plugIn;
			},
			execute : execute
		};
	}

	function doHtmlEscape(at, output) {
		if (at.isEmpty() || at === ZERO_WIDTH)
			return;

		switch (at) {
			case NL_N:
				output.push(JOIN);
				break;
			case SPACE:
				output.push(getSpace());
				break;
			case TAB:
				output.push(HTML_4_NBSP);
				break;
			case LEFT_ANGLE:
				output.push(HTML_LT);
				break;
			case RIGHT_ANGLE:
				output.push(HTML_GT);
				break;
			case SHARP:
				output.push(HTML_SHARP);
				break;
			case AND:
				output.push(HTML_AND);
				break;
			case DQUOTE:
				output.push(HTML_QUOT);
				break;
			default:
				output.push(at);
		}
	}

	var getSpace = (function() {
		var turn = false;
		return function() {
			if (turn) {
				turn = false;
				return SPACE;
			} else {
				turn = true;
				return NBSP;
			}
		};
	})();

	// 功能辅助函数，判断一个字符是否会是数字的一部分
	var NUMBER_SET = /(x|l|d|f)/i;
	function isNumber(at, before, hex) {
		var code = at.charCodeAt(0);
		return (CHAR_CODE_0 <= code && code <= CHAR_CODE_9 || // 是否含有数字
		(hex && (CHAR_CODE_UPPER_A <= code && code <= CHAR_CODE_UPPER_F) || (CHAR_CODE_LOWER_A <= code && code <= CHAR_CODE_LOWER_F))) // 十六进制表达
				|| NUMBER_SET.test(at) || (CHAR_CODE_0 <= before && before <= CHAR_CODE_9 && "." === at); // 可以用于表达数字的字母和符号
	}

	/**
	 * 判断该字符是否可以作为一个词的开头
	 * 词的范围包括：
	 * 变量名、函数名、关键字
	 */
	function canInWord(at) {
		// [A-Z]
		return (CHAR_CODE_UPPER_A <= at && at <= CHAR_CODE_UPPER_Z) ||
		// [a-z]
		(CHAR_CODE_LOWER_A <= at && at <= CHAR_CODE_LOWER_Z) ||
		// [_ $]
		CHAR_CODE_UNDERBAR === at || CHAR_CODE_DOLLAR === at;
	}

	/**
	 * 处理字符或字符串
	 * <p>
	 * 某些语言字符可以包含多个字符，所以这里和字符串一起进行处理。
	 * 输出形式具体是字符还是字符串全权交给每个语言自定义实现
	 * </p>
	 * 
	 * @param code 处理的目标字符串
	 * @param index 处理的起始位置
	 * @param len 字符串长度
	 * @param output 输出对象（数组）
	 * @param escaper 转义字符
	 * @param end  结束标识符
	 * @param charSpann 字符 span 标签，用于区分字符和字符串
	 */
	function defaultDoChars(code, index, len, output, escaper, end, charSpan) {
		var before = escaper;
		for (; index < len; index++) {
			var at = code.charAt(index);
			if (at === NL_N) {
				output.push(CLOSE_SPAN);
				doHtmlEscape(at, output);
				output.push(charSpan);
			} else {
				doHtmlEscape(at, output);
				// 转义字符的转义
				if (at === escaper && before === escaper) {
					before = null;
					continue;
				}
				if (at === end && before !== escaper) {
					break;
				}
			}
			before = at;
		}
		output.push(CLOSE_SPAN);
		return index;
	}

	function defaultDoCharCase(word, charCaseMethod) {
		var outWord = word;
		if (charCaseMethod) {
			switch (typeOf(charCaseMethod)) {
				case 'string':
					outWord = word[charCaseMethod]();
					break;
				case 'function':
					outWord = charCaseMethod.apply(word);
					break;
				default:
					Error.unsupportedType(charCaseMethod);
			}
		}
		return outWord;
	}

	/**
	 * 处理关键字
	 * 
	 * @param kw 每种语言的关键字集
	 * @param word 请求判断的语言
	 * @param next 下一个字符
	 * @param charCaseMethod 大小写标签，因为有些语言不区分大小写，而关键字大小写是固定的，所以这里加入这个函数对被请求的词进行大小写处理
	 *                       这个函数可以是自定义函数，也可以是 JS 既存的字符串处理函数
	 * 
	 */
	function defaultDoKeyword(output, kw, word, next, charCaseMethod) {

		return kw.has(defaultDoCharCase(word)) && !canInWord(next);
	}

	function defaultDoPreset(word, nextCode, next, output, hasDftPreFunc, hasDftPreVar) {
		
		if (canInWord(nextCode)) return false; // 可以入词则返回
		if (isNumber(next, word.lastCode())) return false; // 是数字则返回

		var dftPreFuncFlg = hasDftPreFunc(word), dftPreVarFlg = hasDftPreVar(word);
		var result = (dftPreFuncFlg || dftPreVarFlg);

		if (result) {
			if (dftPreVarFlg) {
				output.push(PRE_VAR_SPAN + word + CLOSE_SPAN);
			}
			if (dftPreFuncFlg) {
				output.push(PRE_FUNC_SPAN + word + CLOSE_SPAN);
			}
		}

		return result;
	}

	function defaultHasDoPreFlg() {
		return false;
	}

	function defaultDoNumber(code, index, len, output) {
		var hex = false;
		var at = code.charAt(index);
		if (canInWord(code.charCodeAt(index - 1))) {
			output.push(at);
			return index;
		}
		var word = NUMBER_SPAN;
		for (; index < len; index++) {
			at = code.charAt(index);
			if (at === 'x' || at === 'X') {
				hex = true;
			}
			if (isNumber(at, code.charCodeAt(index - 1), hex)) {
				word += at;
			} else {
				break;
			}
		}
		output.push(word + CLOSE_SPAN);
		return index - 1;
	}

	function doLineComment(code, index, len, at, output) {
		var word = COMMENT_SPAN;
		for (; index < len; index++) {
			at = code.charAt(index);
			if (at === NL_N) {
				break;
			} else {
				word += at;
			}
		}
		output.push(word + CLOSE_SPAN);
		return --index;
	}

	var BLOCK_COMMENT_END = "*/";
	function doBlockComment4CLike(code, index, len, output, doc) {
		doc = doc && code.charAt(index + 2) === ASTERISK;
		var word = doc ? DOC_SPAN : COMMENT_SPAN;
		for (; index < len; index++) {
			var at = code.charAt(index);
			if (at !== ASTERISK || code.charAt(index + 1) !== SLASH) {
				if (at === NL_N) {
					output.push(word + CLOSE_SPAN + JOIN);
					word = doc ? DOC_SPAN : COMMENT_SPAN;
				} else {
					output.push(word);
					doHtmlEscape(at, output);
					word = BLANK;
				}
			} else {
				break;
			}
		}
		output.push(word + BLOCK_COMMENT_END + CLOSE_SPAN);
		return ++index;
	}

	function judgeComment4CLike(at) {
		return at === SLASH;
	}

	function doComment4CLike(code, index, len, at, output, doc) {
		var next = (index < len - 1) ? code.charAt(index + 1) : BLNAK;
		if (judgeComment4CLike(next)) {
			index = doLineComment(code, index, len, at, output);
		} else if (next === ASTERISK) {
			index = doBlockComment4CLike(code, index, len, output, doc);
		}
		return index;
	}

	function emptyJudgePluginExe() {
		return false;
	}

	function commonExecute(code) {
		var kw = this.getKeywords(), plugIn = this.getPlugIn();
		var word = BLANK, output = [], escaper = ESCAPER_4_C_LIKE, judgeExe = emptyJudgePluginExe, plugInExe = null, hasPlugIn = !!plugIn;
		// 插件的判断函数
		var doComment = doComment4CLike, judgeComment = judgeComment4CLike, hasDftPreFunc = defaultHasDoPreFlg, hasDftPreVar = defaultHasDoPreFlg;
		// 插件的执行函数
		var doKeyword = defaultDoKeyword, doChar = defaultDoChars, doNumber = defaultDoNumber, doPreset = defaultDoPreset;
		// 插件替代量的默认值
		var charCaseMethod = null, charSpan = CHAR_SPAN, doc = false;

		// 插件和可替换（继承）处理的预处理
		if (hasPlugIn) {
			if (plugIn.charSpan) {
				charSpan = plugIn.charSpan;
			}
			if (plugIn.escaper) {
				escaper = plugIn.escaper;
			}
			if (plugIn.doComment) {
				doComment = plugIn.doComment;
			}
			if (plugIn.charCaseMethod) {
				charCaseMethod = plugIn.charCaseMethod;
			}
			if (plugIn.judgeComment) {
				judgeComment = plugIn.judgeComment;
			}
			if (plugIn.judgeExe) {
				judgeExe = plugIn.judgeExe;
				if (plugIn.execute) {
					plugInExe = plugIn.execute;
				}
			}
			if (plugIn.doChar) {
				doChar = plugIn.doChar;
			}
			if (plugIn.doNumber) {
				doNumber = plugIn.doNumber;
			}
			if (plugIn.doKeyword) {
				doKeyword = plugIn.doKeyword;
			}
			if (plugIn.doPreset) {
				doPreset = plugIn.doPreset;
			}
			if (plugIn.hasDftPreFunc) {
				hasDftPreFunc = plugIn.hasDftPreFunc;
			}
			if (plugIn.hasDftPreVar) {
				hasDftPreVar = plugIn.hasDftPreVar;
			}
			if (plugIn.OPERATOR_REGX) {
				operatorRegx = plugIn.OPERATOR_REGX;
			}
			doc = plugIn.doc;
		}

		for ( var index = 0, len = code.length; index < len; index++) {

			var at = code.charAt(index), codeAt = code.charCodeAt(index);
			if (SPACE_REGX.test(at)) { // 标准空白
				output.push(word);
				doHtmlEscape(at, output);
				word = BLANK;
			} else if (hasPlugIn && judgeExe(at)) { // 每个语言的自定义插件
				index = plugInExe(code, index, len, output);
			} else if (judgeComment(at)) { // 注释
				output.push(word);
				index = doComment(code, index, len, at, output, doc);
				word = BLANK;
			} else if (at === DQUOTE) {
				// 双引号，一般来说双引号都都是字符串，所以这里直接写死
				// 以后要是遇到了 双引号不是字符串的，在做修改
				output.push(word);
				output.push(STRING_SPAN);
				index = defaultDoChars(code, index, len, output, escaper, at, STRING_SPAN);
				word = BLANK;
			} else if (at === QUOTE) {
				// 单引号，默认判断为字符，具体实现由各语言自定义的 doChar 方法来实现
				// 即，如果将 doChar 自定义为 doChars ，那单引号也可以被当作字符串来处理
				output.push(word);
				output.push(charSpan);
				index = doChar(code, index, len, output, escaper, at, charSpan);
				word = BLANK;
			} else {
				if (word.isEmpty() && CHAR_CODE_0 <= codeAt && codeAt <= CHAR_CODE_9) { // 数字
					index = doNumber(code, index, len, output);
				} else {
					if (BRACEKT_REGX.test(at)) { // // 合法的括号（不含尖括号）
						output.push(word + BRACKET_SPAN + at + CLOSE_SPAN);
						word = BLANK;
					} else if (at === LEFT_ANGLE) { // 左尖括号
						output.push(word);
						doHtmlEscape(at, output);
						word = BLANK;
					} else {
						word += at;
						var next = code.charCodeAt(index + 1);
						if (OPERATOR_REGX_4_C_LIKE.test(word)) { // 类C语言的操作符
							output.push(word);
							word = BLANK;
						} else if (doKeyword(output, kw, word, next, charCaseMethod)) { // 关键字
							output.push(KEYWORD_SPAN + word + CLOSE_SPAN);
							word = BLANK;
						} else if (doPreset(word, next, code.charAt(index + 1), output, hasDftPreFunc, hasDftPreVar)) { // 语言内置函数、变量等
							word = BLANK;
						}
					}
				}
			}
		}
		return output.join(BLANK);
	}

	LANGUAGES.SQL = getLang(commonExecute, {
		doComment : function(code, index, len, at, output) {
			var next = code.charAt(index + 1);
			if (at === SLASH && next === ASTERISK) {
				index = doBlockComment4CLike(code, index, len, output);
			} else if (at == HYPHON && next === HYPHON) {
				index = doLineComment(code, index, len, at, output);
			} else {
				doHtmlEscape(at, output);
			}
			return index;
		},
		judgeComment : function(at) {
			return at === SLASH || at === HYPHON;
		},
		charSpan : STRING_SPAN,
		doChar : function(code, index, len, output, escaper, end, span) {
			var next = code.charAt(index++), before = escaper;
			for (; index < len; index++) {
				var at = next;
				next = code.charAt(index);
				if (at === NL_N) {
					output.push(CLOSE_SPAN);
					doHtmlEscape(at, output);
					output.push(span);
				} else {
					doHtmlEscape(at, output);
					if (at === end) {
						if (next === escaper) {
							index++;
						} else if (before !== escaper) {
							break;
						}
					}
					before = at;
				}
			}
			output.push(CLOSE_SPAN);
			return --index;
		},
		escaper : QUOTE,
		charCaseMethod : 'toUpperCase'
	}, [ "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT",
			"BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
			"CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
			"DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
			"ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR", "FOREIGN", "FROM",
			"FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY",
			"INNER", "INSERT", "INSTEAD", "INTEGER", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT",
			"LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER",
			"OUTER", "PLAN", "PRAGMA", "PRIMARY", "QUERY", "RAISE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE",
			"RENAME", "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
			"TEMPORARY", "TEXT", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING",
			"VACUUM", "VALUES", "VARCHAR", "VIEW", "VIRTUAL", "WHEN", "WHERE" ]);

	// Java
	(function() {

		var ANNOTATION_SPAN = '<span class="annotation">', AT_INTERFACE = '@interface';

		function doAnnotation(code, index, len, output) {

			var word = BLANK;
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (SPACE_REGX.test(at) || at === '(')
					break;
				word += at;
			}
			if (word === AT_INTERFACE) {
				output.push(KEYWORD_SPAN + word + CLOSE_SPAN);
			} else {
				output.push(ANNOTATION_SPAN + word + CLOSE_SPAN);
			}

			return --index;
		}

		var dftPreFunc = [ 'Object', 'System', 'NullPointerException', 'Boolean', 'Integer', 'main', 'Exception',
				'Throwable', 'Error', 'Class', 'String', 'Long', 'Number', 'Character', 'Short', 'Byte', 'Float',
				'Double', 'Math', 'Date', 'Calendar', 'StringBuffer', 'StringBuilder' ];
		var dftPreVar = [ 'T', 'E', 'K', 'V' ];

		LANGUAGES.Java = getLang(commonExecute, {
			judgeExe : function(at) {
				switch (at) {
					case AT:
						return true;
					default:
						return false;
				}
			},
			hasDftPreFunc : function(word) {
				return dftPreFunc.has(word);
			},
			hasDftPreVar : function(word) {
				return dftPreVar.has(word);
			},
			doc : true,
			execute : function(code, index, len, output) {
				switch (code.charAt(index)) {
					case AT:
						return doAnnotation(code, index, len, output);
					default:
						return index;
				}
			}
		}, [ 'private', 'protected', 'public', 'abstract', 'class', 'extends', 'final', 'implements', 'interface',
				'native', 'new', 'static', 'strictfp', 'synchronized', 'transient', 'volatile', 'break', 'continue',
				'return', 'do', 'while', 'if', 'else', 'for', 'instanceof', 'switch', 'case', 'default', 'assert',
				'catch', 'finally', 'throw', 'throws', 'try', 'import', 'package', 'boolean', 'byte', 'char', 'double',
				'float', 'int', 'long', 'short', 'null', 'true', 'false', 'super', 'this', 'void', 'goto' ]);
	})();

	// js / json
	(function() {

		function doJsRegExp(code, index, len, at, output) {
			var word = at, before = at, hasRegex = false;
			var start = index;
			for (start += 1; start < len; start++) {
				at = code.charAt(start);
				if (at === NL_N) {
					at = JOIN;
					break;
				} else if (at === SLASH && before !== ESCAPER_4_C_LIKE) {
					hasRegex = true;
				} else if (hasRegex && !(at === 'i' || at === 'g' || at === 'm')) {
					break;
				}
				word += at;
				before = at;
			}
			if (hasRegex) {
				output.push(REGEXP_SPAN + word + CLOSE_SPAN);
				start--;
				index = start;
			} else {
				output.push(code.charAt(index));
			}
			return index;
		}

		var dftPreFunc = [ 'eval', 'alert', 'Object', 'String', 'Date', 'Number', 'Math', 'RegExp', 'Function',
				'Error', 'Array', 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'decodeURI', 'decodeURIComponent',
				'encodeURI', 'encodeURIComponent', 'escape', 'unescape' ];
		var dftPreVar = [ 'document', 'window', 'console', 'Infinity', 'NaN' ];

		// json 就是 js ，所以直接引用JS 的实现
		LANGUAGES.JSON = LANGUAGES.JavaScript = getLang(commonExecute, {
			charSpan : STRING_SPAN,
			hasDftPreFunc : function(word) {
				return dftPreFunc.has(word);
			},
			hasDftPreVar : function(word) {
				return dftPreVar.has(word);
			},
			doComment : function(code, index, len, at, output) {
				var next = code.charAt(index + 1);
				var method = (next === SLASH || next === ASTERISK) ? doComment4CLike : doJsRegExp;
				return method(code, index, len, at, output);
			}
		}, [ 'break', 'case', 'catch', 'continue ', 'default', 'delete ', 'do', 'else', 'false', 'finally', 'for',
				'function', 'if', 'in', 'instanceof', 'new', 'null', 'return', 'switch', 'this', 'throw', 'true',
				'try', 'typeof', 'var', 'while', 'with', 'void', 'undefined', 'abstract', 'boolean', 'byte', 'char',
				'class', 'const', 'debugger', 'double', 'enum', 'export', 'extends', 'final', 'float', 'goto',
				'implements', 'import', 'int', 'interface', 'long', 'native', 'package', 'private', 'protected',
				'public', 'short', 'static', 'super', 'synchronized', 'throws', 'transient', 'volatile' ]);
	})();

	// C 语言
	(function() {

		var MACRODEFINE_SPAN = '<span class="macrodefine">';
		var dftPreFunc = [ 'printf', 'malloc', 'free' ];

		function doMacroDefine(code, index, len, output) {

			var before = null;

			for (index; index < len; index++) {
				var at = code.charAt(index);
				if (at === NL_N || (SPACE_REGX.test(at) && before !== SHARP) || at === LEFT_ANGLE || at === DQUOTE) {
					output.push(CLOSE_SPAN);
					doHtmlEscape(at, output);
					return index;
				} else {
					doHtmlEscape(at, output);
					before = at;
				}
			}
		}

		LANGUAGES.C = getLang(commonExecute, {
			judgeExe : function(at) {
				switch (at) {
					case SHARP:
						return true;
					default:
						return false;
				}
			},
			hasDftPreFunc : function(word) {
				return dftPreFunc.has(word);
			},
			OPERATOR_REGX : /(\+|\-|\*|\/|\%|\&|\||\^|\!|\~|\?|\:|,|;|<)/,
			execute : function(code, index, len, output) {
				var at = code.charAt(index);
				switch (at) {
					case SHARP:
						output.push(MACRODEFINE_SPAN);
						output.push(HTML_SHARP);
						return doMacroDefine(code, ++index, len, output);
					default:
						return index;
				}
			}
		}, [ 'auto', 'break', 'case', 'char', 'const', 'continue', 'do', 'default', 'double', 'else', 'enum', 'extern',
				'float', 'for', 'goto', 'if', 'int', 'long', 'register', 'return', 'typedef', 'signed', 'sizeof',
				'short', 'static', 'struct', 'switch', 'union', 'unsigned', 'void', 'volatile', 'while' ]);
	})();

	// XML
	(function() {

		var XML_SH = '!', XML_Q = '?', COMMENT_END = '-->';

		function inComment(code, index, len, output) {
			output.push(COMMENT_SPAN);

			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === NL_N) {
					output.push(CLOSE_SPAN);
					output.push(JOIN);
					output.push(COMMENT_SPAN);
				} else {
					if (at === RIGHT_ANGLE) {
						output.push(HTML_GT);
					} else {
						doHtmlEscape(at, output);
					}
					if (at === HYPHON && code.charAt(index + 1) === HYPHON && code.charAt(index + 2) === RIGHT_ANGLE) {
						output.push(COMMENT_END);
						output.push(CLOSE_SPAN);
						index += 2;
						return index;
					}
				}
			}
		}

		// 因为不清楚实现规则，暂时用 tag 来实现
		function doDoctype(code, index, len, output) {
			return inTag(code, index, len, output);
		}

		function doXmlAttKey(code, index, len, output) {
			output.push(ATTRIBUTE_SPAN);
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === EQUALS || SPACE_REGX.test(at)) {
					output.push(CLOSE_SPAN);
					doHtmlEscape(at, output);
					return index;
				} else {
					doHtmlEscape(at, output);
				}
			}
		}
		function doXmlAttVal(code, index, len, output) {
			output.push(DATAVALUE_SPAN);
			var before = ESCAPER_4_C_LIKE;
			for (; index < len; index++) {
				var at = code.charAt(index);
				doHtmlEscape(at, output);
				if (at === DQUOTE && before !== ESCAPER_4_C_LIKE)
					break;
				before = at;
			}
			output.push(CLOSE_SPAN);
			return index;
		}

		function inTag(code, index, len, output) {

			output.push(XMLTAG_SPAN);
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === RIGHT_ANGLE) {
					output.push(HTML_GT);
					output.push(CLOSE_SPAN);
					return index;
				}
				if (SPACE_REGX.test(at))
					break;
				doHtmlEscape(at, output);
			}
			output.push(CLOSE_SPAN);

			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === RIGHT_ANGLE) {
					output.push(XMLTAG_SPAN);
					output.push(HTML_GT);
					output.push(CLOSE_SPAN);
					return index;
				}
				if ((at === XML_SH || at === SLASH || at === XML_Q) && code.charAt(++index) === RIGHT_ANGLE) {
					output.push(XMLTAG_SPAN);
					output.push(at);
					output.push(HTML_GT);
					output.push(CLOSE_SPAN);
					return index;
				} else if (SPACE_REGX.test(at)) {
					doHtmlEscape(at, output);
				} else if (at === DQUOTE) {
					index = doXmlAttVal(code, index, len, output);
				} else {
					index = doXmlAttKey(code, index, len, output);
				}
			}
		}

		function doXML(code) {

			var output = [];
			for ( var index = 0, len = code.length; index < len; index++) {
				var at = code.charAt(index);
				if (at === LEFT_ANGLE) {
					if (code.charAt(index + 1) === XML_SH) {
						if (code.charAt(index + 2) === HYPHON) {
							index = inComment(code, index, len, output);
						} else {
							index = doDoctype(code, index, len, output);
						}
					} else {
						index = inTag(code, index, len, output);
					}
				} else {
					doHtmlEscape(at, output);
				}
			}
			return output.join(BLANK);
		}

		LANGUAGES.XML = {
			execute : doXML
		};
	})();

	// CSS
	(function() {

		var CSSNAME_SPAN = '<span class="cssname">', CSSVALUE_SPAN = '<span class="cssvalue">', CSS_XMLTAG_SPAN = '<span class="css_xmltag">';

		function inName(code, index, len, output) {
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === LEFT_BRACKET) {
					return index;
				}
				if (at === SHARP || at === POINT) {
					index = cssName(code, index, len, output);
				} else {
					index = xmlTag(code, index, len, output);
				}
			}
		}

		function inBody(code, index, len, output) {

			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === RIGHT_BRACKET) {
					output.push(at);
					return index;
				} else {
					var charCode = code.charCodeAt(index);
					if ((CHAR_CODE_UPPER_A <= charCode && charCode <= CHAR_CODE_UPPER_Z)
							|| (CHAR_CODE_LOWER_A <= charCode && charCode <= CHAR_CODE_LOWER_Z)) {
						index = cssAttr(code, index, len, output);
						if (index < len) {
							index = cssValue(code, index, len, output);
						} else {
							index = cssSpace(code, index, len, output);
						}
						// index--;
					} else {
						doHtmlEscape(at, output);
					}
				}
			}
		}

		function cssName(code, index, len, output) {

			output.push(CSSNAME_SPAN);
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === COMMA || SPACE_REGX.test(at)) {
					output.push(CLOSE_SPAN);
					doHtmlEscape(at, output);
					return index;
				} else if (at === LEFT_BRACKET) {
					output.push(CLOSE_SPAN);
					return --index;
				} else {
					doHtmlEscape(at, output);
				}
			}
		}

		function xmlTag(code, index, len, output) {

			output.push(CSS_XMLTAG_SPAN);
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === COMMA || SPACE_REGX.test(at)) {
					output.push(CLOSE_SPAN);
					doHtmlEscape(at, output);
					return index;
				} else if (at === LEFT_BRACKET) {
					output.push(CLOSE_SPAN);
					return --index;
				} else {
					doHtmlEscape(at, output);
				}
			}

		}

		function cssValue(code, index, len, output) {

			index = cssSpace(code, index, len, output);

			output.push(CSSVALUE_SPAN);
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === SEMICOLON || at === RIGHT_BRACKET) {
					output.push(CLOSE_SPAN);
					output.push(at);
					return index;
				}
				if (at === NL_N) {
					output.push(CLOSE_SPAN);
					doHtmlEscape(at, output);
					output.push(CSSVALUE_SPAN);
				} else {
					doHtmlEscape(at, output);
				}
			}
		}

		function cssSpace(code, index, len, output) {

			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === SLASH) {
					index = doBlockComment4CLike(code, index, len, output);
				} else if (at === COMMA || SPACE_REGX.test(at)) {
					doHtmlEscape(at, output);
				} else {
					return index;
				}
			}
		}

		function cssAttr(code, index, len, output) {

			output.push(ATTRIBUTE_SPAN);
			for (; index < len; index++) {
				var at = code.charAt(index);
				if (at === COLON) {
					output.push(CLOSE_SPAN);
					output.push(at);
					return ++index;
				}
				if (at === NL_N) {
					output.push(CLOSE_SPAN);
					doHtmlEscape(at, output);
					output.push(ATTRIBUTE_SPAN);
				} else {
					doHtmlEscape(at, output);
				}
			}
		}

		function doCSS(code) {

			var len = code.length, index = 0, output = [];
			var at = code.charAt(index);
			if (at === AT) {
				for (; index < len; index++) {
					at = code.charAt(index);
					output.push(at);
					if (at === SEMICOLON) {
						index++;
						break;
					}
				}
			}

			for (; index < len; index++) {
				at = code.charAt(index);
				if (at === SLASH) {
					index = doBlockComment4CLike(code, index, len, output);
				} else if (at === COMMA || SPACE_REGX.test(at)) {
					doHtmlEscape(at, output);
				} else {
					index = inName(code, index, len, output);
					index = inBody(code, index, len, output);
				}
			}

			return output.join(BLANK);

		}

		LANGUAGES.CSS = {
			execute : doCSS
		};
	})();

	// HTML
	(function() {

		var xml = LANGUAGES.XML, css = LANGUAGES.CSS, js = LANGUAGES.JavaScript;
		var JS_START = "<script", JS_END = "</script>", CSS_START = "<style", CSS_END = "</style>";
		var JS_END_LEN = JS_END.length, CSS_END_LEN = CSS_END.length;

		function doCSS(input) {
			var cssStart = input.indexOf(CSS_START);

			if (cssStart < 0)
				return xml.execute(input);

			cssStart += input.slice(cssStart).indexOf(">") + 1;
			var cssEnd = input.indexOf(CSS_END);

			return xml.execute(input.slice(0, cssStart)) + css.execute(input.slice(cssStart, cssEnd))
					+ xml.execute(CSS_END) + doCSS(input.slice(cssEnd + CSS_END_LEN));
		}

		function doHTML(input) {

			var jsStart = input.indexOf(JS_START);

			if (jsStart < 0)
				return doCSS(input);

			jsStart += input.slice(jsStart).indexOf(">") + 1;
			var jsEnd = input.indexOf(JS_END);

			var output = doCSS(input.slice(0, jsStart)) + js.execute(input.slice(jsStart, jsEnd)) + xml.execute(JS_END)
					+ doHTML(input.slice(jsEnd + JS_END_LEN));
			return output;
		}

		LANGUAGES.HTML = {
			execute : doHTML
		};

	})();

	// PHP
	(function() {
		var dftPreFunc = [ 'eval' ];

		var PHP_START = "<?php", PHP_END = "?>";
		var PHP_START_LEN = PHP_START.length, PHP_END_LEN = PHP_END.length;

		var html = LANGUAGES.HTML, xml = LANGUAGES.XML, php = getLang(commonExecute, {
			charSpan : STRING_SPAN,
			hasDftPreFunc : function(word) {
				return dftPreFunc.has(word);
			}
		}, [ 'and', 'or', 'xor', 'exception', 'as', 'break', 'case', 'class', 'const', 'continue ', 'declare',
				'default', 'do', 'echo', 'else', 'elseif', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch',
				'endwhile', 'extends', 'for', 'foreach', 'function', 'global', 'if', 'include', 'include_once', 'new',
				'print', 'require', 'require_once', 'return', 'static', 'switch', 'use', 'var', 'while', 'final',
				'php_user_filter', 'interface', 'implements', 'public', 'private', 'protected', 'abstract', 'clone ',
				'try', 'catch', 'throw', 'cfunction', 'this', '__LINE__', '__FUNCTION__', '__CLASS__', '__METHOD__' ]);

		function doPHP(input) {

			var phpStart = input.indexOf(PHP_START);

			if (phpStart < 0)
				return php.execute(input);

			phpStart += PHP_START_LEN;

			var phpEnd = input.indexOf(PHP_END);

			var output = html.execute(input.slice(0, phpStart)) + php.execute(input.slice(phpStart, phpEnd))
					+ xml.execute(PHP_END) + doPHP(input.slice(phpEnd + PHP_END_LEN));
			return output;
		}

		LANGUAGES.PHP = {
			execute : doPHP
		};

	})();

	// VB / VBA / VBScript
	(function() {

		var dftPreFunc = [ 'LBound', 'UBound', 'MsgBox', 'Val', 'Chr', 'UCase', 'InStr', 'CreateObject', 'Len',
				'Replace', 'Left', 'Right' ];

		LANGUAGES.VBScript = LANGUAGES.VBA = LANGUAGES.VB = getLang(commonExecute, {
			judgeComment : function(at) {
				return at === QUOTE;
			},
			hasDftPreFunc : function(word) {

				var outWord = defaultDoCharCase(word), result = false;
				dftPreFunc.forEach(function(wd) {
					if (outWord.equalsIgnoreCase(wd)) {
						result = true;
						return;
					}
				});

				return result;
			},
			doComment : doLineComment,
			escaper : DQUOTE,
			doKeyword : function(output, kw, word, next, charCaseMethod) {

				var outWord = defaultDoCharCase(word), result = false;
				kw.forEach(function(wd) {
					if (outWord.equalsIgnoreCase(wd)) {
						result = true;
						return;
					}
				});

				return result && !canInWord(next);
			}
		}, [ "AddHandler", "AddressOf", "Alias", "And", "AndAlso", "Ansi", "As", "Assembly", "Auto", "Boolean",
				"ByRef", "Byte", "ByVal", "Call", "Case", "Catch", "CBool", "CByte", "CChar", "CDate", "CDec", "CDbl",
				"Char", "CInt", "Class", "CLng", "CObj", "Const", "CShort", "CSng", "CStr", "CType", "Date", "Decimal",
				"Declare", "Default", "Delegate", "Dim", "DirectCast", "Do", "Double", "Each", "Else", "ElseIf", "End",
				"Enum", "Erase", "Error", "Event", "Exit", "#ExternalSource", "False", "Finally", "For", "Friend",
				"Function", "Get", "GetType", "GoTo", "Handles", "If", "Implements", "Imports", "In", "Inherits",
				"Integer", "Interface", "Is", "Let", "Lib", "Like", "Long", "Loop", "Me", "Mod", "Module",
				"MustInherit", "MustOverride", "MyBase", "MyClass", "Namespace", "New", "Next", "Not", "Nothing",
				"NotInheritable", "NotOverridable", "Object", "On", "Option", "Optional", "Or", "OrElse", "Overloads",
				"Overridable", "Overrides", "ParamArray", "Preserve", "Private", "Property", "Protected", "Public",
				"RaiseEvent", "ReadOnly", "ReDim", "#Region", "REM", "RemoveHandler", "Resume", "Return", "Select",
				"Set", "Shadows", "Shared", "Short", "Single", "Static", "Step", "Stop", "String", "Structure", "Sub",
				"SyncLock", "Then", "Throw", "To", "True", "Try", "TypeOf", "Unicode", "Until", "Variant", "When",
				"While", "With", "WithEvents", "WriteOnly", "Xor", "#Const", "#ExternalSource", "#Region" ]);
	})();

	var START_TAG = '[code=', TAG_CLOSE = ']', END_TAG = '[/code' + TAG_CLOSE, NEW_LINE = /(\r\n|\n|\r)/ig, SPACE_4 = /    /g;
	var START_LEN = START_TAG.length, CLOSE_LEN = TAG_CLOSE.length, END_LEN = END_TAG.length;
	var FILED_START = '<fieldset class="code fieldset"><legend class="legend">', FILED_LIST = ' 代码</legend><div><ol type="1"><li>', FILED_END = '</li></ol></div></fieldset>';
	function execute(input) {

		if (!input) {
			return BLANK;
		}

		input = input.replace(NEW_LINE, NL_N);

		var start = input.indexOf(START_TAG), end = input.indexOf(END_TAG);
		if (start >= 0 && start < end) {
			var tagClose = start + START_LEN;
			for (; tagClose < end; tagClose++) {
				if (input.charAt(tagClose) === TAG_CLOSE) {
					break;
				}
			}
			if (tagClose === end) {
				return input;
			}

			var lang = input.slice(start + START_LEN, tagClose);
			if (lang.isEmpty()) {
				return input;
			}
			var language = LANGUAGES[lang];
			if (language) {
				var code = input.slice(start + START_LEN + lang.length + CLOSE_LEN, end);
				code = code.replace(SPACE_4, TAB); // 将所有四个空格替换为制表符

				return [ input.slice(0, start), FILED_START + lang + FILED_LIST + language.execute(code) + FILED_END,
						execute(input.slice(end + END_LEN)) ];
			} else {
				return input;
			}
		} else {
			return input;
		}
	}

	// 对外接口
	var that = {
		addLang : function(lang) {
			LANGUAGES[lang] = getLang.apply(null, arraySlice.call(arguments, 1));
		},
		getLangs : function() {
			return Object.keys(LANGUAGES).sort();
		},
		execute : execute,
		//		commonExecute : commonExecute,
		defaultDoChars : defaultDoChars,
		defaultDoNumber : defaultDoNumber,
		judgeComment4CLike : judgeComment4CLike,
		doComment4CLike : doComment4CLike,
		doBlockComment4CLike : doBlockComment4CLike,
		doHtmlEscape : doHtmlEscape,
		isNumber : isNumber,
		canInWord : canInWord,
		defaultDoPreset : defaultDoPreset,
		defaultHasDoPreFlg : defaultHasDoPreFlg,
		CLOSE_SPAN : CLOSE_SPAN,
		COMMENT_SPAN : COMMENT_SPAN,
		KEYWORD_SPAN : KEYWORD_SPAN,
		BRACKET_SPAN : BRACKET_SPAN,
		STRING_SPAN : STRING_SPAN,
		NUMBER_SPAN : NUMBER_SPAN,
		CHAR_SPAN : CHAR_SPAN,
		REGEXP_SPAN : REGEXP_SPAN,
		OPERATOR_SPAN : OPERATOR_SPAN,
		XMLTAG_SPAN : XMLTAG_SPAN,
		ATTRIBUTE_SPAN : ATTRIBUTE_SPAN,
		DATAVALUE_SPAN : DATAVALUE_SPAN,
		DOC_SPAN : DOC_SPAN,
		PRE_FUNC_SPAN : PRE_FUNC_SPAN,
		PRE_VAR_SPAN : PRE_VAR_SPAN,
		HTML_QUOT : HTML_QUOT,
		HTML_4_NBSP : HTML_4_NBSP,
		HTML_LT : HTML_LT,
		HTML_GT : HTML_GT,
		HTML_AND : HTML_AND,
		HTML_SHARP : HTML_SHARP,
		AT : AT,
		AND : AND,
		SPACE : SPACE,
		TAB : TAB,
		SHARP : SHARP,
		SLASH : SLASH,
		DQUOTE : DQUOTE,
		QUOTE : QUOTE,
		ASTERISK : ASTERISK,
		COMMA : COMMA,
		SEMICOLON : SEMICOLON,
		COLON : COLON,
		LEFT_BRACKET : LEFT_BRACKET,
		RIGHT_BRACKET : RIGHT_BRACKET,
		LEFT_ANGLE : LEFT_ANGLE,
		RIGHT_ANGLE : RIGHT_ANGLE,
		POINT : POINT,
		EQUALS : EQUALS,
		HYPHON : HYPHON,
		ZERO_WIDTH : ZERO_WIDTH,
		SPACE_REGX : SPACE_REGX,
		BRACEKT_REGX : BRACEKT_REGX
	};

	if (Eureka.side()) { // 这里是判断客户端还是浏览器端，自行实现
		window.FlyHighLighter = that;
	} else {
		module.exports = that;
	}
})();